Micron Document
C.S.Burner 🪙 ━►🔥GIT Node


Displaying Raw • Download

meshchatx/src/frontend/components/tools/BotsPage.vue 65ca9ddc823fcc2c8f8653c553b944b85db0875f (65ca9ddc) Text, 36.47 KB

T8b949e<!-- SPDX-License-Identifier: 0BSD -->

Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col flex-1 overflow-hidden min-w-0 bg-slate-50 dark:bg-zinc-950Ta5d6ff"Tff7b72>
Tff7b72<ToolsPageHeader
Te6edf3icon=Ta5d6ff"Ta5d6ffrobotTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6fftools.bots.titleTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3:description=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6fftools.bots.descriptionTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3accent=Ta5d6ff"Ta5d6ffblueTa5d6ff"
Tff7b72/>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 overflow-y-auto w-full px-3 sm:px-4 md:px-5 lg:px-8 py-4 sm:py-6 pb-[max(1.5rem,env(safe-area-inset-bottom))]Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-8 w-full max-w-4xl mx-autoTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.create_new_bot") }}
Tff7b72</h3>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffgrid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6fftemplate in templatesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fftemplate.idTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrelative rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-4 hover:border-blue-400 dark:hover:border-blue-600 transition cursor-pointer flex flex-col justify-between min-h-[140px] pr-12Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectTemplate(template)Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>{{ template.name }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-400 mt-1Ta5d6ff"Tff7b72>
{{ template.description }}
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute bottom-3 right-3 p-2 rounded-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colors pointer-events-noneTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffchevron-rightTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-dashed border-gray-300 dark:border-zinc-700 bg-gray-50/50 dark:bg-zinc-900/50 p-4 flex flex-col items-center justify-center min-h-[140px] opacity-70Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-2 bg-gray-100 dark:bg-zinc-800 rounded-lg mb-2Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffplusTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-6 text-gray-400 dark:text-gray-500Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-medium text-gray-500 dark:text-gray-400 text-centerTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.more_bots_coming") }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.saved_bots") }}
Tff7b72</h3>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffbots.length === 0Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-500 italicTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.no_bots_running") }}
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2 sm:space-y-3Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ffbot in botsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffbot.idTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff[
Ta5d6ff'Ta5d6ffrelative rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-3 sm:p-4Ta5d6ff'Ta5d6ff,
editingBotId === bot.id ? Ta5d6ff'Ta5d6ffpr-28 sm:pr-40Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffpr-10 sm:pr-12Ta5d6ff'Ta5d6ff,
]Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute top-2 right-2 flex flex-wrap items-center justify-end gap-0.5 z-10 max-w-[min(100%,calc(100%-2rem))]Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6fflxmfAddressFor(bot)Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.chat_with_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffopenChatWithBot(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffmessage-textTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-amber-600 dark:hover:text-amber-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.force_announceTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffforceAnnounce(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffbullhornTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.restart_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffrestartExisting(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrefreshTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.stop_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstopBot(bot.id)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffstopTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ff!bot.runningTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-emerald-600 dark:hover:text-emerald-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.start_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartExisting(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffplayTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-violet-600 dark:hover:text-violet-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.view_process_logTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffopenProcessLog(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffbug-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.export_identityTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffexportIdentity(bot.id)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffexportTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.delete_botTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdeleteBot(bot.id)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffdeleteTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>

Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start gap-3 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg shrink-0Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffrobotTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-6 text-blue-600 dark:text-blue-400Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 flex-1 space-y-1.5 sm:pr-2Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-1 min-w-0Ta5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff editingBotId === bot.id
? Ta5d6ff'Ta5d6ffmax-w-[min(100%,14rem)] sm:max-w-[16rem]Ta5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6ff'
Ta5d6ff Ta5d6ff"
Tff7b72>
Tff7b72<template Te6edf3v-if=Ta5d6ff"Ta5d6ffeditingBotId === bot.idTa5d6ff"Tff7b72>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffeditingNameDraftTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-field text-xs py-1 h-8 px-2 min-w-0 flex-1 max-w-40 sm:max-w-48Ta5d6ff"
Te6edf3maxlength=Ta5d6ff"Ta5d6ff256Ta5d6ff"
@keydown.enter.Te6edf3prevent=Ta5d6ff"Ta5d6ffsaveBotName(bot)Ta5d6ff"
@keydown.Te6edf3escape=Ta5d6ff"Ta5d6ffcancelEditNameTa5d6ff"
Tff7b72/>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1 rounded-lg text-gray-500 hover:text-emerald-600 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffcommon.saveTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveBotName(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcheckTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffcommon.cancelTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcancelEditNameTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</template>
Tff7b72<template v-elseTff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-gray-900 dark:text-white truncateTa5d6ff"Tff7b72>{{
bot.name
}}Tff7b72</span>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 rounded-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 shrink-0Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.edit_nameTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartEditName(bot)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpencilTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</template>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 text-[11px] text-gray-600 dark:text-gray-300Ta5d6ff"
Tff7b72>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6ffinline-block size-2 rounded-full shrink-0Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ffbot.running ? Ta5d6ff'Ta5d6ffbg-emerald-500Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffbg-gray-400 dark:bg-gray-500Ta5d6ff'Ta5d6ff"
Tff7b72>Tff7b72</span>
Tff7b72<spanTff7b72>{{
bot.running ? Te6edf3$t("bots.status_running") : Te6edf3$t("bots.status_stopped")
}}Tff7b72</span>
Tff7b72</div>
Tff7b72<dl Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2.5 text-[11px] text-gray-600 dark:text-gray-300 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<dt
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("bots.lxmf_address") }}
Tff7b72</dt>
Tff7b72<dd Te6edf3class=Ta5d6ff"Ta5d6ffm-0 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6fflxmfAddressFor(bot)Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffont-mono text-[11px] break-all text-left w-full max-w-full text-gray-800 dark:text-gray-200 hover:underline leading-snugTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcopyLxmfAddress(bot)Ta5d6ff"
Tff7b72>
{{ lxmfAddressFor(bot) }}
Tff7b72</button>
Tff7b72<span v-else Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-500 dark:text-gray-400Ta5d6ff"Tff7b72>{{
Te6edf3$t("bots.address_pending")
}}Tff7b72</span>
Tff7b72</dd>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<dt
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("bots.last_announce") }}
Tff7b72</dt>
Tff7b72<dd Te6edf3class=Ta5d6ff"Ta5d6ffm-0 text-gray-700 dark:text-gray-200 leading-snugTa5d6ff"Tff7b72>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffbot.last_announce_atTa5d6ff"Tff7b72>{{
formatRelativeSince(bot.last_announce_at)
}}Tff7b72</span>
Tff7b72<span Te6edf3v-else-if=Ta5d6ff"Ta5d6fflxmfAddressFor(bot)Ta5d6ff"Tff7b72>{{
Te6edf3$t("bots.never_announced")
}}Tff7b72</span>
Tff7b72<span v-elseTff7b72>-Tff7b72</span>
Tff7b72</dd>
Tff7b72</div>
Tff7b72</dl>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffbotLastError(bot)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-red-200/90 dark:border-red-900/70 bg-red-50/90 dark:bg-red-950/50 px-2.5 py-2 text-[11px] text-red-900 dark:text-red-100Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-semibold flex items-center gap-1.5Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffalert-circle-outlineTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsize-4 shrink-0 opacity-90Ta5d6ff"
Tff7b72/>
{{ Te6edf3$t("bots.last_error_heading") }}
Tff7b72</div>
Tff7b72<pre
Te6edf3class=Ta5d6ff"Ta5d6ffmt-1.5 m-0 whitespace-pre-wrap break-words font-mono text-[10px] leading-relaxed text-red-800/95 dark:text-red-100/90Ta5d6ff"
Tff7b72>{{ botLastError(bot) }}Tff7b72</pre>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] text-gray-400 pt-0.5Ta5d6ff"Tff7b72>
{{ bot.template_id || bot.template }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffprocessLogModalBotTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffixed inset-0 z-100 flex items-end sm:items-center justify-center p-0 sm:p-4 bg-black/50Ta5d6ff"
@click.Te6edf3self=Ta5d6ff"Ta5d6ffcloseProcessLogTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffw-full sm:max-w-3xl flex flex-col max-sm:h-[92dvh] max-sm:max-h-[92dvh] sm:max-h-[90vh] rounded-t-2xl sm:rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 shadow-xl touch-pan-y min-h-0Ta5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-between items-start gap-2 p-3 sm:p-5 border-b border-gray-200 dark:border-zinc-800 shrink-0Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0 pr-2Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg sm:text-xl font-bold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("bots.process_log_title") }}
Tff7b72</h3>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-400 mt-0.5 truncateTa5d6ff"Tff7b72>
{{ processLogModalBot.name }}
Tff7b72</p>
Tff7b72<p Te6edf3v-if=Ta5d6ff"Ta5d6ffprocessLogTruncatedTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-amber-700 dark:text-amber-400 mt-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("bots.process_log_truncated") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-1 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80Ta5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffbots.copy_process_logTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ff!processLogTextTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcopyProcessLogTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcontent-copyTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffcloseProcessLogTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-h-0 flex flex-col p-2 sm:p-5 pt-2 sm:pt-2Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffprocessLogLoadingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-center py-16 text-gray-500 dark:text-gray-400 text-smTa5d6ff"
Tff7b72>
{{ Te6edf3$t("bots.process_log_loading") }}
Tff7b72</div>
Tff7b72<div
v-else
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-h-0 max-sm:min-h-[55dvh] sm:min-h-[12rem] overflow-auto rounded-lg border border-gray-200 dark:border-zinc-800 bg-gray-50 dark:bg-zinc-900 touch-pan-xTa5d6ff"
Tff7b72>
Tff7b72<pre
Te6edf3class=Ta5d6ff"Ta5d6ffbots-process-log-text m-0 min-h-full w-max min-w-full p-2 sm:p-3 font-mono text-gray-800 dark:text-gray-200 whitespace-pre select-textTa5d6ff"
Tff7b72>{{ processLogDisplayText }}Tff7b72</pre>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffselectedTemplateTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffixed inset-0 z-100 flex items-end sm:items-center justify-center p-0 sm:p-4 bg-black/50Ta5d6ff"
@click.Te6edf3self=Ta5d6ff"Ta5d6ffselectedTemplate = nullTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffw-full sm:max-w-md rounded-t-2xl sm:rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-4 sm:p-6 space-y-4 max-h-[90vh] overflow-y-autoTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-between items-start gap-2Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg sm:text-xl font-bold text-gray-900 dark:text-white pr-2Ta5d6ff"Tff7b72>
{{ Te6edf3$t("bots.start_bot") }}: {{ selectedTemplate.name }}
Tff7b72</h3>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectedTemplate = nullTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>

Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4Ta5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<label Te6edf3class=Ta5d6ff"Ta5d6ffglass-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("bots.bot_name") }}Tff7b72</label>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffnewBotNameTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Ta5d6ffselectedTemplate.nameTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinput-fieldTa5d6ff"
Tff7b72/>
Tff7b72</div>

Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-400Ta5d6ff"Tff7b72>
{{ selectedTemplate.description }}
Tff7b72</div>

Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex justify-end gap-2 pt-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100/80 dark:hover:bg-zinc-800/80Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectedTemplate = nullTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 rounded-lg text-gray-500 hover:text-emerald-600 hover:bg-gray-100/80 dark:hover:bg-zinc-800/80 disabled:opacity-40Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffisStartingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartBotTa5d6ff"
Tff7b72>
Tff7b72<span
Te6edf3v-if=Ta5d6ff"Ta5d6ffisStartingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-block w-5 h-5 border-2 border-current border-t-transparent rounded-full animate-spinTa5d6ff"
Tff7b72>Tff7b72</span>
Tff7b72<MaterialDesignIcon v-else Te6edf3icon-name=Ta5d6ff"Ta5d6ffcheckTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>

Tff7b72<scriptTff7b72>
import ToastUtils from "../../js/ToastUtils";
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import ToolsPageHeader from "./ToolsPageHeader.vue";

export default {
name: "BotsPage",
components: {
MaterialDesignIcon,
ToolsPageHeader,
},
data() {
return {
bots: [],
templates: [],
selectedTemplate: null,
newBotName: "",
isStarting: false,
loading: true,
refreshInterval: null,
relativeTimerTick: 0,
relativeTimerInterval: null,
editingBotId: null,
editingNameDraft: "",
processLogModalBot: null,
processLogText: "",
processLogTruncated: false,
processLogLoading: false,
};
},
computed: {
processLogDisplayText() {
const t = (this.processLogText || "").trim();
if (t) {
return this.processLogText;
}
return this.processLogLoading ? "" : this.$t("bots.process_log_empty");
},
},
mounted() {
this.getStatus();
this.refreshInterval = setInterval(this.getStatus, 5000);
this.relativeTimerInterval = setInterval(() => {
this.relativeTimerTick += 1;
}, 1000);
},
beforeUnmount() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
if (this.relativeTimerInterval) {
clearInterval(this.relativeTimerInterval);
}
},
methods: {
async getStatus() {
try {
const response = await window.api.get("/api/v1/bots/status");
this.bots = response.data.status.bots || [];
this.templates = response.data.templates;
this.loading = false;
} catch (e) {
console.error("[BotsPage] getStatus failed", e?.response?.data || e?.message || e);
}
},
selectTemplate(template) {
this.selectedTemplate = template;
this.newBotName = template.name;
},
async startBot() {
if (this.isStarting) return;
this.isStarting = true;
try {
await window.api.post("/api/v1/bots/start", {
template_id: this.selectedTemplate.id,
name: this.newBotName,
});
ToastUtils.success(this.$t("bots.bot_started"));
this.selectedTemplate = null;
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.failed_to_start"));
} finally {
this.isStarting = false;
}
},
async stopBot(botId) {
try {
await window.api.post("/api/v1/bots/stop", { bot_id: botId });
ToastUtils.success(this.$t("bots.bot_stopped"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("bots.failed_to_stop"));
}
},
async startExisting(bot) {
try {
await window.api.post("/api/v1/bots/start", {
bot_id: bot.id,
template_id: bot.template_id || bot.template,
name: bot.name,
});
ToastUtils.success(this.$t("bots.bot_started"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.failed_to_start"));
}
},
async restartExisting(bot) {
try {
await window.api.post("/api/v1/bots/restart", { bot_id: bot.id });
ToastUtils.success(this.$t("bots.bot_started"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.failed_to_start"));
}
},
async deleteBot(botId) {
if (!confirm(this.$t("common.delete_confirm"))) return;
try {
await window.api.post("/api/v1/bots/delete", { bot_id: botId });
ToastUtils.success(this.$t("bots.bot_deleted"));
if (this.editingBotId === botId) {
this.cancelEditName();
}
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("bots.failed_to_delete"));
}
},
exportIdentity(botId) {
window.open(`/api/v1/bots/export?bot_id=${botId}`, "_blank");
},
startEditName(bot) {
this.editingBotId = bot.id;
this.editingNameDraft = bot.name || "";
},
cancelEditName() {
this.editingBotId = null;
this.editingNameDraft = "";
},
async saveBotName(bot) {
if (this.editingBotId !== bot.id) {
return;
}
const name = (this.editingNameDraft || "").trim();
if (!name) {
ToastUtils.error(this.$t("bots.name_required"));
return;
}
try {
await window.api.patch("/api/v1/bots/update", {
bot_id: bot.id,
name,
});
ToastUtils.success(this.$t("bots.bot_renamed"));
this.cancelEditName();
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.rename_failed"));
}
},
async forceAnnounce(bot) {
try {
await window.api.post("/api/v1/bots/announce", { bot_id: bot.id });
ToastUtils.success(this.$t("bots.announce_triggered"));
this.getStatus();
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.announce_failed"));
}
},
botLastError(bot) {
const t = (bot?.last_error || "").trim();
return t;
},
closeProcessLog() {
this.processLogModalBot = null;
this.processLogText = "";
this.processLogTruncated = false;
this.processLogLoading = false;
},
async openProcessLog(bot) {
this.processLogModalBot = bot;
this.processLogText = "";
this.processLogTruncated = false;
this.processLogLoading = true;
try {
const response = await window.api.get("/api/v1/bots/subprocess-log", {
params: { bot_id: bot.id },
});
this.processLogText =
response.data.log === null || response.data.log === undefined ? "" : String(response.data.log);
this.processLogTruncated = Boolean(response.data.truncated);
} catch (e) {
console.error(e);
ToastUtils.error(e.response?.data?.message || this.$t("bots.process_log_failed"));
this.closeProcessLog();
} finally {
this.processLogLoading = false;
}
},
copyProcessLog() {
const t = (this.processLogText || "").trim();
if (!t) {
return;
}
navigator.clipboard.writeText(this.processLogText);
ToastUtils.success(this.$t("bots.process_log_copied"));
},
lxmfAddressFor(bot) {
const raw = bot.lxmf_address || bot.full_address;
if (!raw || typeof raw !== "string") {
return "";
}
const h = raw.trim().toLowerCase();
return h.length === 32 && /^[0-9a-f]+$/.test(h) ? h : "";
},
openChatWithBot(bot) {
const h = this.lxmfAddressFor(bot);
if (!h) {
return;
}
const routeName = this.$route?.meta?.isPopout ? "messages-popout" : "messages";
this.$router.push({ name: routeName, params: { destinationHash: h } });
},
copyLxmfAddress(bot) {
const h = this.lxmfAddressFor(bot);
if (!h) {
return;
}
navigator.clipboard.writeText(h);
ToastUtils.success(this.$t("translator.copied_to_clipboard"));
},
formatRelativeSince(iso) {
if (!iso) {
return "";
}
let t;
try {
t = new Date(iso).getTime();
} catch {
return String(iso);
}
if (Number.isNaN(t)) {
return String(iso);
}
const now = Date.now() + 0 * this.relativeTimerTick;
let sec = Math.floor((now - t) / 1000);
if (sec Tff7b72< 0) {
Te6edf3sec = Ta5d6ff0;
}
if (sec < 60) {
return `${sec}s`;
}
const Te6edf3min = Ta5d6ffMath.floor(sec / 60);
if (min < 60) {
return Te6edf3min =Ta5d6ff== 1 ? `${min}m` : `${min}m`;
}
const Te6edf3h = Ta5d6ffMath.floor(min / 60);
if (h < 24) {
return Te6edf3h =Ta5d6ff== 1 ? `${h}h` : `${h}h`;
}
const Te6edf3d = Ta5d6ffMath.floor(h / 24);
if (d < 30) {
return Te6edf3d =Ta5d6ff== 1 ? "1 day" : `${d} days`;
}
const Te6edf3mo = Ta5d6ffMath.floor(d / 30);
if (d < 365) {
return Te6edf3mo =Ta5d6ff== 1 ? "1 month" : `${mo} months`;
}
const Te6edf3y = Ta5d6ffMath.floor(d / 365);
return Te6edf3y =Ta5d6ff== 1 ? "1 year" : `${y} years`;
},
},
};
</scriptTff7b72>

Tff7b72<style scopedTff7b72>
@reference "../../style.css";
.glass-label {
@apply block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1;
}
.bots-process-log-text {
font-size: 0.6875rem;
line-height: 1.55;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
@media (max-width: 639px) {
.bots-process-log-text {
font-size: 0.5625rem;
line-height: 1.28;
}
}
Tff7b72</style>

Served by rngit 1.5.2 - Generated in 0.06s